﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using COFE.Core.Utils;
using COFE.Core;

namespace COFE.Core.Validater
{
    /// <summary>
    /// Validate type of PropertyPair's value.
    /// </summary>
    public class PropertyTypeAttribute : Attribute, IPropertyValidater 
    {
        public enum DateTimeDefaultEnum { None, Min, Max, UtcNow, Now };

        public Type[] Types { get { return _types.ToArray(); } }
        public object DefaultValue { get { return getDefaultValue(); } }

        private object _defaultValue = null;
        private DateTimeDefaultEnum _defDateTimeValue;
        private List<Type> _types = new List<Type>();

        public PropertyTypeAttribute(DateTimeDefaultEnum defaultDateTimeValue)
        {
            _types.Clear();
            _types.Add(typeof(DateTime));
            _defDateTimeValue = defaultDateTimeValue;
        }

        public PropertyTypeAttribute(object defaultValue)
        {
            _types.Clear();
            _types.Add(defaultValue.GetType());
            _defaultValue = defaultValue;
        }

        public PropertyTypeAttribute(object defaultValue, params Type[] types)
            : this(types)
        {            
            _defaultValue = defaultValue;
        }

        public PropertyTypeAttribute(params Type[] types)
        {
            if (types.Length > 1 && types[0] == null) //Missed the above constructor.
            {                
                _types.Clear();
                _types.AddRange(types);
                _types.RemoveAt(0);
                _defaultValue = null;
                return;
            }

            if (types != null)
                _types = new List<Type>(types);
            _defaultValue = null;
        }

        object getDefaultValue()
        {
            if (_defaultValue != null)
                return _defaultValue;
            if (_types.Count > 0 && _types[0] == typeof(DateTime) && _defDateTimeValue != DateTimeDefaultEnum.None)
                switch (_defDateTimeValue)
                {
                    case DateTimeDefaultEnum.Min: return DateTime.MinValue;
                    case DateTimeDefaultEnum.Max: return DateTime.MaxValue;
                    case DateTimeDefaultEnum.Now: return DateTime.Now;
                    case DateTimeDefaultEnum.UtcNow: return DateTime.UtcNow; 
                }
            return null;
        }

        public void Verify(object value)
        {
            if (value == null)
                return;

            if (Types.Length == 0)
                return;

            bool match = false;
            foreach (var t in Types)
            {                
                if (t.IsInstanceOfType(value))
                //if (t.Equals(value.GetType()))
                    match = true;
            }
            Assert.IsTrue(match, value.GetType().ToString() + " != " + Types[0].ToString());
        }
    }

    public static class PropertyTypeExtension
    {
        public static Type GetDefaultPropertyType(object property)
        {
            Type[] pType = GetPropertyType(property);
            if (pType != null && pType.Length > 0)
                return pType[0];
            else return null;
        }

        public static Type[] GetPropertyType(object property)
        {
            var pta = EnumUtils<PropertyTypeAttribute>.FindAttribute(property);
            if (pta != null)
                return pta.Types;
            else return null;
        }

        public static bool HasDefaultValue(object property)
        {
            var pta = EnumUtils<PropertyTypeAttribute>.FindAttribute(property);
            if (pta != null)
                return pta.DefaultValue != null;
            return false;
        }

        public static object GetDefaultValue(object property)
        {
            var pta = EnumUtils<PropertyTypeAttribute>.FindAttribute(property);
            if (pta != null)
                return pta.DefaultValue;
            else return null;
        }

        public static PropertyPair GetDefaultValueAsPropertyPair(object property)
        {
            return new PropertyPair(property, GetDefaultValue(property));
        }

    }
}
